package cn.sccl.dirms.topo.discovery.util;

import cn.sccl.dirms.topo.snmp.mib.IfEntry;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Created by julei on 2017/9/28.
 */
public class Test {
    public static void main(String[] args) throws InterruptedException {
        List<String> ips = new ArrayList<String>();
        ips.add("10.206.10.65");
        ips.add("10.206.10.80");
//        ips.add("10.206.10.81");
//        ips.add("10.206.10.83");
//        ips.add("10.206.10.93");
        Map<String, Map<Integer, List<IfPoint>[]>> ipInOutLineMap = shortCutDevPortPackage(ips);
        match("10.206.10.65", 14, ipInOutLineMap);

    }

    public static void match(String matchIp, int matchPort, Map<String, Map<Integer, List<IfPoint>[]>> ipInOutLineMap) {
        List<IfPoint>[] matchInOutLine = ipInOutLineMap.get(matchIp).get(matchPort);
        String bestMatchIp = "";
        int bestMatchPort = -1;
        double minScore = -1;
        ipInOutLineMap.remove(matchIp);
        for (Map.Entry<String, Map<Integer, List<IfPoint>[]>> entry : ipInOutLineMap.entrySet()) {
            String ip = entry.getKey();
            Map<Integer, List<IfPoint>[]> portInOutLineMap = entry.getValue();
            for (Map.Entry<Integer, List<IfPoint>[]> portEntry : portInOutLineMap.entrySet()) {
                int port = portEntry.getKey();
                List<IfPoint>[] inoutLine = portEntry.getValue();
                double score = matchInOutLine(matchInOutLine, inoutLine);
                System.out.println(ip + ":" + port + ":" + score);
                if (minScore == -1) {
                    minScore = score;
                }
                if (score < minScore) {
                    bestMatchIp = ip;
                    bestMatchPort = port;
                    minScore = score;
                }
            }
        }
        System.out.println(matchIp + ":" + matchPort + "-------" + bestMatchIp + ":" + bestMatchPort + "--" + minScore);
    }

    public static double matchInOutLine(List<IfPoint>[] matchA, List<IfPoint>[] matchB) {
        List<IfPoint> inA = matchA[0];
        List<IfPoint> outA = matchA[1];
        List<IfPoint> inB = matchB[0];
        List<IfPoint> outB = matchB[1];
        double scoreIn = matchLine(inA, outB);
        double scoreOut = matchLine(outA, inB);
        return scoreIn + scoreOut;
    }


    public static double matchLine(List<IfPoint> matchA, List<IfPoint> matchB) {
        double score = 0;
        int sizeA = matchA.size();
        int sizeB = matchB.size();
        //找出所有的拐点，形成线段
        Long sameBeginX = 0L;
        int AbeginIndex = 0;
        int BbeginIndex = 0;
        A:
        for (int a = 0; a < sizeA; a++) {
            IfPoint ifPointA = matchA.get(a);
            for (int b = 0; b < sizeB; b++) {
                IfPoint ifPointB = matchB.get(b);
                if (ifPointA.x == ifPointB.x) {
                    AbeginIndex = a;
                    BbeginIndex = b;
                    sameBeginX = ifPointA.x;
                    break A;
                }
            }
        }
        //相同开始的两个曲线图
        matchA = matchA.subList(AbeginIndex, sizeA - 1);
        matchB = matchB.subList(BbeginIndex, sizeB - 1);
        //以一分钟为间隔,截取出N段比较样本
        List<List<IfPoint>> one_miniute_line_A = new ArrayList<List<IfPoint>>();
        List<List<IfPoint>> one_miniute_line_B = new ArrayList<List<IfPoint>>();
        sizeA = matchA.size();
        sizeB = matchB.size();

        for (int i = 0; i < (sizeA / 60) - 1; i++) {
            one_miniute_line_A.add(matchA.subList((i * 60), ((i + 1) * 60) - 1));
        }
        for (int i = 0; i < (sizeB / 60) - 1; i++) {
            one_miniute_line_B.add(matchB.subList((i * 60), ((i + 1) * 60) - 1));
        }
        sizeA = one_miniute_line_A.size();
        sizeB = one_miniute_line_A.size();
        int matchTimes=0;
        for (int i = 0; i < sizeA; i++) {
            if (i < sizeB) {
                List<IfPoint> lineAPoints = one_miniute_line_A.get(i);
                List<IfPoint> lineBPoints = one_miniute_line_B.get(i);
                //生成线段模型
                IfLine lineA = new IfLine(lineAPoints);
                IfLine lineB = new IfLine(lineBPoints);
                score = score + Math.abs(lineA.getDistance() - lineB.getDistance());
                matchTimes++;
            }
        }
        return score/matchTimes;
    }

    /**
     * 读取所有设备一段时间内流量快照 2292690523
     *
     * @param deviceInfos
     * @return
     */
    public static Map<String, Map<Integer, List<IfPoint>[]>> shortCutDevPortPackage(List<String> deviceInfos) {
        Map<String, List<List<IfEntry>>> result = portPackagePool(deviceInfos);
        Map<String, Map<Integer, List<IfPoint>[]>> ipPortInOutPkgsMap = new HashMap<String, Map<Integer, List<IfPoint>[]>>();
        for (Map.Entry<String, List<List<IfEntry>>> ipTimeIfEntry : result.entrySet()) {
            String ip = ipTimeIfEntry.getKey();
            Map<Integer, List<IfPoint>[]> portInOutPkgs = getAvgPackIncrease(ipTimeIfEntry.getValue());
            ipPortInOutPkgsMap.put(ip, portInOutPkgs);
        }
        return ipPortInOutPkgsMap;
    }

    /**
     * 求取各端口 采集时间段内包传输总数
     *
     * @return
     */
    public static Map<Integer, List<IfPoint>[]> getAvgPackIncrease(List<List<IfEntry>> timeIfListMapList) {
        Map<Integer, List<IfPoint>[]> result = new HashMap<Integer, List<IfPoint>[]>();
        //采集了20次
        //形成二维数组,模拟曲线
        //曲线近似度最近的数组表示可能为相同端口
        if (timeIfListMapList == null || timeIfListMapList.size() == 0) {
            return result;
        }
        //生成端口--曲线map
        Map<Integer, List<IfPoint>> portLineInMap = new LinkedHashMap<Integer, List<IfPoint>>();
        Map<Integer, List<IfPoint>> portLineOutMap = new LinkedHashMap<Integer, List<IfPoint>>();

        for (List<IfEntry> ifEntries : timeIfListMapList) {
            for (IfEntry ifEntry : ifEntries) {
                List<IfPoint> portsIn = portLineInMap.get(ifEntry.getIfIndex());
                List<IfPoint> portsOut = portLineOutMap.get(ifEntry.getIfIndex());
                if (portsIn == null) {
                    portsIn = new ArrayList<IfPoint>();
                }
                if (portsOut == null) {
                    portsOut = new ArrayList<IfPoint>();
                }
                portsIn.add(new IfPoint(ifEntry.getDataTime(), ifEntry.getIfInOctets()));
                portsOut.add(new IfPoint(ifEntry.getDataTime(), ifEntry.getIfOutOctets()));

                portLineInMap.put(ifEntry.getIfIndex(), portsIn);
                portLineOutMap.put(ifEntry.getIfIndex(), portsOut);
            }
        }
        for (Map.Entry<Integer, List<IfPoint>> inIfPointListEntry : portLineInMap.entrySet()) {
            List<IfPoint> lineList = createLine(inIfPointListEntry.getValue());
            portLineInMap.put(inIfPointListEntry.getKey(), lineList);
        }
        for (Map.Entry<Integer, List<IfPoint>> outIfPointListEntry : portLineOutMap.entrySet()) {
            List<IfPoint> lineList = createLine(outIfPointListEntry.getValue());
            result.put(outIfPointListEntry.getKey(), new List[]{portLineInMap.get(outIfPointListEntry.getKey()), lineList});
        }
        return result;
    }

    public static List<IfPoint> createLine(List<IfPoint> IfPoints) {
        List<IfPoint> lineIfPointList = new ArrayList<IfPoint>();
        int length = IfPoints.size() - 1;
        for (int i = 0; i < length; i++) {
            IfPoint IfPoint = IfPoints.get(i);
            IfPoint IfPointNext = IfPoints.get(i + 1);
            Long x1 = IfPoint.x, x2 = IfPointNext.x;
            double y1 = IfPoint.y, y2 = IfPointNext.y;
            if (x1 == x2) {
                // Tangent = NaN
                double from = Math.min(y1, y2);
                double to = Math.max(y1, y2);
                for (double y = from; y <= to; y++) {
                    lineIfPointList.add(new IfPoint(x1, y));
                }
            } else {
                double slope = ((y2 - y1)) / ((double) (x2 - x1));
                int step = (x2 > x1) ? 1000 : -1000;
                for (Long x = x1; x <= x2; x += step) {
                    double y = ((x - x1) * slope + y1);
                    lineIfPointList.add(new IfPoint(x, y));
                }
            }
        }
        return lineIfPointList;
    }


    /**
     * 多线程去取端口性能数据
     *
     * @param ips
     * @return
     */
    public static Map<String, List<List<IfEntry>>> portPackagePool(List<String> ips) {
        int threadNum = 1000;
        Map<String, List<List<IfEntry>>> result = new HashMap<String, List<List<IfEntry>>>();
        ExecutorService service = Executors.newFixedThreadPool(threadNum);
        for (int i = 0; i < ips.size(); ) {
            String ip = ips.get(i);
            service.execute(new TestPortPacakgeTask(ip, result));
            i++;
            if (i % threadNum == 0) {
                if (!service.isShutdown()) {
                    service.shutdown();
                }
                try {
                    for (boolean bool = false; !bool; bool = service.awaitTermination(40, TimeUnit.SECONDS)) ;
                    service = Executors.newFixedThreadPool(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (!service.isShutdown()) {
            service.shutdown();
        }
        try {
            for (boolean bool = false; !bool; bool = service.awaitTermination(40, TimeUnit.SECONDS)) ;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

}
